package cn.iocoder.yudao.module.product.evaluation.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.business.basic.pojo.vo.UserSimpleVO;
import cn.iocoder.yudao.framework.business.evaluation.enums.ScoreLevelEnum;
import cn.iocoder.yudao.framework.business.evaluation.enums.ScoreTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.api.member.user.MemberUserApi;
import cn.iocoder.yudao.module.api.product.evaluation.dto.ProductEvaluationCreateReqDTO;
import cn.iocoder.yudao.module.api.shop.info.ShopInfoApi;
import cn.iocoder.yudao.module.api.trade.order.TradeOrderApi;
import cn.iocoder.yudao.module.api.trade.order.dto.TradeOrderItemRespDTO;
import cn.iocoder.yudao.module.product.evaluation.bo.ShopIdCommentCountBO;
import cn.iocoder.yudao.module.product.evaluation.bo.ShopIdScoreBO;
import cn.iocoder.yudao.module.product.evaluation.bo.SpuIdScoreBO;
import cn.iocoder.yudao.module.product.evaluation.convert.ProductEvaluationConvert;
import cn.iocoder.yudao.module.product.evaluation.dal.dataobject.ProductEvaluationDO;
import cn.iocoder.yudao.module.product.evaluation.dal.mysql.ProductEvaluationMapper;
import cn.iocoder.yudao.module.product.evaluation.vo.*;
import jakarta.annotation.Nullable;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.api.product.ErrorCodeConstants.*;

/**
 * 产品评价 Service 实现类
 *
 * @author wangzhs
 */
@Service
@Validated
public class ProductEvaluationServiceImpl implements ProductEvaluationService {

    @Resource
    private ProductEvaluationMapper productEvaluationMapper;

    @Resource
    private MemberUserApi memberUserApi;
    @Resource
    private TradeOrderApi tradeOrderApi;
    @Resource
    private ShopInfoApi shopInfoApi;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createComment(ProductEvaluationCreateReqVO reqVO, boolean virtual) {
        // 机器人评论
        List<UserSimpleVO> robots = memberUserApi.getUserListByNickname(reqVO.getUserNickname());
        Long userId;
        if (CollUtil.isEmpty(robots)) {
            userId = memberUserApi.createRobot(reqVO.getUserNickname(), reqVO.getUserAvatar());
        } else {
            userId = robots.get(0).getId();
        }
        TradeOrderItemRespDTO orderItem = tradeOrderApi.createVirtualOrderComment(userId, reqVO.getSkuId());
        ProductEvaluationCreateReqDTO reqDTO = BeanUtils.toBean(reqVO, ProductEvaluationCreateReqDTO.class);
        reqDTO.setOrderItemId(orderItem.getId());
        createComment0(reqDTO, orderItem, userId, virtual);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long createComment(Long userId, AppProductEvaluationCreateReqVO reqVO) {
        if (reqVO.getOrderItemId() != null && reqVO.getOrderItemId() > 0) {
            // 校验评论
            validateCommentNotExistsByUserIdAndOrderItemId(getLoginUserId(), reqVO.getOrderItemId());
            // 校验订单
            TradeOrderItemRespDTO orderItem = tradeOrderApi.getOrderItem(reqVO.getOrderItemId());
            if (orderItem == null) {
                throw exception(COMMENT_ORDER_NOT_EXISTS);
            }
            return createComment0(reqVO, orderItem, userId, false);
        }
        Long lastCommentId = 0L;
        if (reqVO.getOrderId() != null && reqVO.getOrderId() > 0) {
            // 校验评论
            validateCommentNotExistsByUserIdAndOrderId(getLoginUserId(), reqVO.getOrderId());
            // 校验订单
            List<TradeOrderItemRespDTO> orderItems = tradeOrderApi.getOrderItemsByOrderId(reqVO.getOrderId());
            if (CollUtil.isEmpty(orderItems)) {
                throw exception(COMMENT_ORDER_NOT_EXISTS);
            }
            for (TradeOrderItemRespDTO orderItem : orderItems) {
                lastCommentId = createComment0(reqVO, orderItem, userId, false);
            }
        }
        return lastCommentId;
    }

    private Long createComment0(ProductEvaluationCreateReqDTO dto, TradeOrderItemRespDTO orderItem, Long userId, boolean virtual) {
        // 插入
        ProductEvaluationDO comment = ProductEvaluationConvert.INSTANCE.convert(dto, orderItem);
        if (dto.getScores() != null && dto.getScores() > 0) {
            comment.setScores(dto.getScores());
        }
        comment.setShopId(orderItem.getShopId());
        comment.setUserId(userId);
        comment.setIsVirtual(virtual);
        productEvaluationMapper.insert(comment);
        shopInfoApi.updateScoreWhenEvaluation(orderItem.getShopId());
        return comment.getId();
    }

    @Override
    public void appendComment(AppProductEvaluationAppendReqVO appendReqVO) {
        // 校验评论是否存在
        ProductEvaluationDO commentDO = validateCommentExistsByUserIdAndId(getLoginUserId(), appendReqVO.getId());
        // 追加评论
        ProductEvaluationDO updateObj = BeanUtils.toBean(appendReqVO, ProductEvaluationDO.class);
        updateObj.setId(commentDO.getId());
        productEvaluationMapper.updateById(updateObj);
    }

    private ProductEvaluationDO validateCommentExistsByUserIdAndId(Long userId, Long id) {
        ProductEvaluationDO exist = productEvaluationMapper.selectByUserIdAndId(userId, id);
        if (exist == null) {
            throw exception(COMMENT_NOT_EXISTS);
        }
        return exist;
    }

    private void validateCommentNotExistsByUserIdAndOrderId(Long userId, Long orderId) {
        ProductEvaluationDO exist = productEvaluationMapper.selectByUserIdAndOrderId(userId, orderId);
        if (exist != null) {
            throw exception(COMMENT_EXISTS);
        }
    }

    private void validateCommentNotExistsByUserIdAndOrderItemId(Long userId, Long orderItemId) {
        ProductEvaluationDO exist = productEvaluationMapper.selectByUserIdAndOrderItemId(userId, orderItemId);
        if (exist != null) {
            throw exception(COMMENT_EXISTS);
        }
    }

    @Override
    public void updateCommentVisible(ProductEvaluationUpdateVisibleReqVO updateReqVO) {
        // 校验评论是否存在
        ProductEvaluationDO commentDO = validateCommentExists(updateReqVO.getId());
        // 更新可见状态
        ProductEvaluationDO updateObj = new ProductEvaluationDO();
        updateObj.setId(commentDO.getId());
        updateObj.setVisible(updateReqVO.getVisible());
        productEvaluationMapper.updateById(updateObj);
    }

    @Override
    public void replyComment(ProductEvaluationReplyReqVO replyVO) {
        // 校验评论是否存在
        ProductEvaluationDO commentDO = validateCommentExists(replyVO.getId());
        // 回复评论
        ProductEvaluationDO updateObj = new ProductEvaluationDO();
        updateObj.setId(commentDO.getId());
        updateObj.setReplyTime(LocalDateTime.now());
        updateObj.setReplyUserId(getLoginUserId());
        updateObj.setReplyStatus(Boolean.TRUE);
        updateObj.setReplyContent(replyVO.getReplyContent());
        productEvaluationMapper.updateById(updateObj);
    }

    @Override
    public void replyComment(AppProductEvaluationReplyReqVO replyVO) {
        // 校验评论是否存在
        ProductEvaluationDO commentDO = validateCommentExists(replyVO.getId());
        // 回复评论
        ProductEvaluationDO updateObj = new ProductEvaluationDO();
        updateObj.setId(commentDO.getId());
        updateObj.setReplyTime(LocalDateTime.now());
        updateObj.setReplyUserId(getLoginUserId());
        updateObj.setReplyStatus(Boolean.TRUE);
        updateObj.setReplyContent(replyVO.getReplyContent());
        productEvaluationMapper.updateById(updateObj);
    }

    private ProductEvaluationDO validateCommentExists(Long id) {
        ProductEvaluationDO productComment = productEvaluationMapper.selectById(id);
        if (productComment == null) {
            throw exception(COMMENT_NOT_EXISTS);
        }
        return productComment;
    }

    @Override
    public float calculateScoreByShopId(Long shopId) {
        float score;
        if (shopId == null || shopId <= 0) {
            score = ScoreLevelEnum.STAR_5.getValue();
        } else {
            score = productEvaluationMapper.avgScoreByShopId(shopId);
        }
        return keepOneDot(score);
    }

    private float keepOneDot(float score) {
        return NumberUtil.parseFloat(String.format("%.1f", score));
    }

    @Override
    public Map<Long, Float> batchCalculateScoreByShopIds(Set<Long> shopIds) {
        if (CollUtil.isEmpty(shopIds)) {
            return new HashMap<>();
        }
        List<ShopIdScoreBO> list = productEvaluationMapper.avgScoreByShopIds(shopIds);
        list.forEach(item -> item.setScore(keepOneDot(item.getScore())));
        return convertMap(list, ShopIdScoreBO::getShopId, ShopIdScoreBO::getScore);
    }

    @Override
    public float calculateScoreBySpuId(Long spuId) {
        float score;
        if (spuId == null || spuId <= 0) {
            score = ScoreLevelEnum.STAR_5.getValue();
        } else {
            score = productEvaluationMapper.avgScoreBySpuId(spuId);
        }
        return keepOneDot(score);
    }

    @Override
    public Map<Long, Float> batchCalculateScoreBySpuIds(Set<Long> spuIds) {
        if (CollUtil.isEmpty(spuIds)) {
            return new HashMap<>();
        }
        List<SpuIdScoreBO> list = productEvaluationMapper.avgScoreBySpuIds(spuIds);
        list.forEach(item -> item.setScore(keepOneDot(item.getScore())));
        return convertMap(list, SpuIdScoreBO::getSpuId, SpuIdScoreBO::getScore);
    }

    @Override
    public PageResult<ProductEvaluationDO> getCommentPage(ProductEvaluationPageReqVO pageReqVO) {
        return productEvaluationMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<ProductEvaluationDO> getCommentPage(AppProductEvaluationPageReqVO pageVO, Boolean visible) {
        return productEvaluationMapper.selectPage(pageVO, visible);
    }

    @Override
    public PageResult<ProductEvaluationDO> getMyCommentPage(Long userId, PageParam pageVO) {
        return productEvaluationMapper.selectPage(userId, pageVO);
    }

    @Override
    public ProductEvaluationDO getLatestCommentBySpuId(Long spuId) {
        return productEvaluationMapper.selectLatestCommentBySpuId(spuId);
    }

    @Override
    public Integer getCount(Long shopId, ScoreTypeEnum scoreType, @Nullable Boolean hasPictures) {
        return productEvaluationMapper.selectCount(shopId, scoreType, hasPictures);
    }

    @Override
    public void deleteComment(Long userId, Long id) {
        ProductEvaluationDO commentDO = productEvaluationMapper.selectByUserIdAndId(userId, id);
        if (commentDO == null) {
            throw exception(COMMENT_NOT_EXISTS);
        }
        productEvaluationMapper.deleteById(commentDO.getId());
    }

    @Override
    public Map<Long, Integer> getCommentCountByShopIds(Set<Long> shopIds) {
        if (CollUtil.isEmpty(shopIds)) {
            return new HashMap<>();
        }
        List<ShopIdCommentCountBO> list = productEvaluationMapper.selectCommentCountByShopIds(shopIds);
        return convertMap(list, ShopIdCommentCountBO::getShopId, ShopIdCommentCountBO::getCommentCount);
    }

}
